package org.spring.bean.factory.config;

import org.spring.bean.PropertyValues;
import org.spring.bean.exceptions.BeansException;

import java.beans.PropertyDescriptor;

/**
 * {@link BeanPostProcessor}的子接口，它添加了一个实例化前的回调，以及一个实例化后但在显式属性设置或自动装配发生之前的回调。
 * <p>
 * 通常用于抑制特定目标bean的默认实例化，例如创建具有特殊targetsource(池化目标、惰性初始化目标等)的代理，或者实现额外的注入策略，如字段注入。
 * <p>
 * <b>注:<b>该接口是一个特殊用途的接口，主要用于框架内部使用。
 * 建议尽可能地实现普通的{@link BeanPostProcessor}接口，或者从{@link InstantiationAwareBeanPostProcessorAdapter}派生，以便屏蔽该接口的扩展。
 *
 * @author 李坤
 * @date 2022/4/23 23:28
 */
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {

    /**
     * 在目标bean实例化<i>之前应用这个BeanPostProcessor <i>。
     * 返回的bean对象可能是要使用的代理，而不是目标bean，从而有效地抑制了目标bean的默认实例化。
     * <p>
     * 如果此方法返回一个非空对象，则bean创建过程将会短路。
     * 唯一的进一步处理是来自已配置的{@link BeanPostProcessor}的{@link #postProcessAfterInitialization}回调。
     * <p>
     * 这个回调将应用于bean定义及其bean类，以及工厂方法定义，在这种情况下，返回的bean类型将被传递到这里。
     * <p>
     * 后处理程序可以实现扩展的{@link SmartInstantiationAwareBeanPostProcessor}接口，以预测它们将返回的bean对象的类型。
     * <p>
     * 默认实现返回{@code null}。
     *
     * @param beanClass 要实例化的bean的类
     * @param beanName  bean的名称
     *
     * @return 要公开的bean对象而不是目标bean的默认实例，或者进行默认实例化的{@code null}
     *
     * @throws org.springframework.beans.BeansException 以防出现错误
     * @see #postProcessAfterInstantiation
     * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getBeanClass()
     * @see org.springframework.beans.factory.support.AbstractBeanDefinition#getFactoryMethodName()
     */
    default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        return null;
    }

    /**
     * 在bean实例化之后(通过构造函数或工厂方法)，但在Spring属性填充(通过显式属性或自动装配)发生之前执行操作。
     * <p>
     * 这是在Spring的自动装配启动之前，在给定的bean实例上执行自定义字段注入的理想回调。
     * <p>
     * 默认实现返回{@code true}。
     *
     * @param bean     创建的bean实例，其属性尚未设置
     * @param beanName bean的名称
     *
     * @return {@code true}如果属性应该设置在bean上；{@code false}，如果属性填充应该被跳过。正常的实现应该返回{@code true}。
     * 返回{@code false}也会阻止在这个bean实例上调用任何后续的InstantiationAwareBeanPostProcessor实例。
     *
     * @throws org.springframework.beans.BeansException 以防出现错误
     * @see #postProcessBeforeInstantiation
     */
    default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }

    /**
     * 在工厂将给定的属性值应用到给定bean之前，对它们进行后处理，而不需要属性描述符。
     * <p>
     * 如果实现提供了自定义的{@link postProcessPropertyValues}实现，则应该返回{@code null}(默认)，否则返回{@code pvs}。
     * 在该接口的未来版本中(移除{@link postProcessPropertyValues})，默认实现将直接返回给定的{@code pvs} as-is。
     *
     * @param pvs      工厂将要应用的属性值(从不使用{@code null})
     * @param bean     已创建的bean实例，但尚未设置其属性
     * @param beanName bean的名称
     *
     * @return 应用于给定bean的实际属性值(可以是传入的PropertyValues实例)，或者{@code null}继续处理现有的属性，但具体来说，
     * 继续调用{@link postProcessPropertyValues}(当前bean类需要初始化的{@code PropertyDescriptor})
     *
     * @throws org.springframework.beans.BeansException 以防出现错误
     * @see #postProcessPropertyValues
     * @since 5.1
     */
    default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
            throws BeansException {
        return null;
    }

    /**
     * 在工厂将给定的属性值应用到给定bean之前，对它们进行后处理。
     * 允许检查是否满足所有依赖项，例如基于bean属性设置器上的“Required”注释。
     * <p>
     * 还允许替换要应用的属性值，通常通过基于原始PropertyValues创建新的MutablePropertyValues实例，添加或删除特定值。
     * <p>
     * 默认实现按原样返回给定的{@code propertyValues}。
     *
     * @param propertyValues      工厂将要应用的属性值(从不使用{@code null})
     * @param propertyDescriptors 目标bean的相关属性描述符(带有被忽略的依赖类型—工厂专门处理—已经过滤掉了)
     * @param bean                已创建的bean实例，但尚未设置其属性
     * @param beanName            bean的名称
     *
     * @return 应用于给定bean的实际属性值(可以是传入的PropertyValues实例)，或跳过属性填充的{@code null}
     *
     * @throws org.springframework.beans.BeansException 以防出现错误
     * @see #postProcessProperties
     * @see org.springframework.beans.MutablePropertyValues
     * @deprecated 从5.1开始，支持{@link #postProcessProperties(PropertyValues, Object, String)}
     */
    @Deprecated
    default PropertyValues postProcessPropertyValues(PropertyValues propertyValues, PropertyDescriptor[] propertyDescriptors,
                                                     Object bean, String beanName) throws BeansException {
        return propertyValues;
    }

}
